Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Displaying Raw • Download


meshchatx/src/backend/http/routes/lxmf.py e41daa5b65e07a5baa1aaad32d97f90482f1cd1c (e41daa5b) Text, 54.88 KB

T8b949e# SPDX-License-Identifier: 0BSD
T8b949e"""HTTP routes: lxmf."""

Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations


Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787httpT7ee787.T7ee787meshchat_names Tff7b72import Tb4b4b4( T8b949e# noqa: F401
Te6edf3GeoValidationErrorTb4b4b4,
Te6edf3OutboundHttpBlockedErrorTb4b4b4,
Te6edf3OverlayExportErrorTb4b4b4,
Te6edf3OverlaySourceParseErrorTb4b4b4,
Te6edf3PluginSecurityErrorTb4b4b4,
Te6edf3AsyncUtilsTb4b4b4,
Te6edf3InterfaceConfigParserTb4b4b4,
Te6edf3InterfaceDiscoveryTb4b4b4,
Te6edf3InterfaceEditorTb4b4b4,
Te6edf3LOGIN_PATHTb4b4b4,
Te6edf3LXMFTb4b4b4,
Te6edf3LxmfAudioFieldTb4b4b4,
Te6edf3LxmfFileAttachmentTb4b4b4,
Te6edf3LxmfFileAttachmentsFieldTb4b4b4,
Te6edf3LxmfImageFieldTb4b4b4,
Te6edf3MAX_EXPORT_TILESTb4b4b4,
Te6edf3MarkdownRendererTb4b4b4,
Te6edf3NomadnetFileDownloaderTb4b4b4,
Te6edf3NomadnetPageDownloaderTb4b4b4,
Te6edf3RNProbeHandlerTb4b4b4,
Te6edf3RNSTb4b4b4,
Te6edf3ReticulumMeshChatTb4b4b4,
Te6edf3SETUP_PATHTb4b4b4,
Te6edf3TRANSPARENT_TILETb4b4b4,
Te6edf3TelemeterTb4b4b4,
Te6edf3UTCTb4b4b4,
Te6edf3WSMsgTypeTb4b4b4,
Te6edf3_is_chaquopy_androidTb4b4b4,
Te6edf3_is_loopback_bind_hostTb4b4b4,
Te6edf3_request_client_ipTb4b4b4,
Te6edf3aiohttpTb4b4b4,
Te6edf3app_versionTb4b4b4,
Te6edf3assert_migration_context_pathsTb4b4b4,
Te6edf3asyncioTb4b4b4,
Te6edf3base64Tb4b4b4,
Te6edf3bcryptTb4b4b4,
Te6edf3binasciiTb4b4b4,
Te6edf3build_blocklist_export_documentTb4b4b4,
Te6edf3build_export_documentTb4b4b4,
Te6edf3build_messages_export_bundleTb4b4b4,
Te6edf3cache_statsTb4b4b4,
Te6edf3cancel_inbound_deliveriesTb4b4b4,
Te6edf3castTb4b4b4,
Te6edf3compute_lxmf_conversation_unread_from_latest_rowTb4b4b4,
Te6edf3configparserTb4b4b4,
Te6edf3contextlibTb4b4b4,
Te6edf3convert_db_favourite_to_dictTb4b4b4,
Te6edf3convert_db_lxmf_message_to_dictTb4b4b4,
Te6edf3convert_lxmf_message_to_dictTb4b4b4,
Te6edf3convert_nomadnet_field_data_to_mapTb4b4b4,
Te6edf3convert_nomadnet_string_data_to_mapTb4b4b4,
Te6edf3convert_propagation_node_state_to_stringTb4b4b4,
Te6edf3copyTb4b4b4,
Te6edf3datetimeTb4b4b4,
Te6edf3describe_port_conflictTb4b4b4,
Te6edf3detect_image_format_from_magicTb4b4b4,
Te6edf3ensure_outbound_http_allowedTb4b4b4,
Te6edf3ensure_session_csrf_tokenTb4b4b4,
Te6edf3filter_announced_dicts_by_search_queryTb4b4b4,
Te6edf3fresh_storage_at_targetTb4b4b4,
Te6edf3get_cached_active_linkTb4b4b4,
Te6edf3get_file_pathTb4b4b4,
Te6edf3get_sessionTb4b4b4,
Te6edf3get_trusted_proxy_cidrsTb4b4b4,
Te6edf3gif_utilsTb4b4b4,
Te6edf3i2p_supportTb4b4b4,
Te6edf3import_messages_export_bundleTb4b4b4,
Te6edf3ioTb4b4b4,
Te6edf3is_mbtiles_filenameTb4b4b4,
Te6edf3is_path_within_dirTb4b4b4,
Te6edf3is_port_in_useTb4b4b4,
Te6edf3is_user_facing_lxmf_payloadTb4b4b4,
Te6edf3jsonTb4b4b4,
Te6edf3list_host_network_interfacesTb4b4b4,
Te6edf3list_inbound_deliveriesTb4b4b4,
Te6edf3list_portsTb4b4b4,
Te6edf3load_app_security_settingsTb4b4b4,
Te6edf3loggerTb4b4b4,
Te6edf3loggingTb4b4b4,
Te6edf3lxmf_sidebar_preview_for_conversation_latest_rowTb4b4b4,
Te6edf3memory_log_handlerTb4b4b4,
Te6edf3message_fields_have_attachmentsTb4b4b4,
Te6edf3migrate_legacy_to_targetTb4b4b4,
Te6edf3mime_for_image_typeTb4b4b4,
Te6edf3normalize_identity_storage_hashTb4b4b4,
Te6edf3normalize_lxmf_sieve_filtersTb4b4b4,
Te6edf3normalize_message_blocklistTb4b4b4,
Te6edf3osTb4b4b4,
Te6edf3parse_bool_query_paramTb4b4b4,
Te6edf3parse_import_documentTb4b4b4,
Te6edf3parse_lxmf_display_nameTb4b4b4,
Te6edf3parse_lxmf_propagation_node_app_dataTb4b4b4,
Te6edf3parse_lxmf_sieve_filters_jsonTb4b4b4,
Te6edf3parse_lxmf_stamp_costTb4b4b4,
Te6edf3parse_message_blocklist_jsonTb4b4b4,
Te6edf3parse_nomadnetwork_node_display_nameTb4b4b4,
Te6edf3platformTb4b4b4,
Te6edf3privacy_mode_enabledTb4b4b4,
Te6edf3psutilTb4b4b4,
Te6edf3purge_messages_before_cutoffTb4b4b4,
Te6edf3reTb4b4b4,
Te6edf3resolve_message_age_cutoffTb4b4b4,
Te6edf3reticulum_pathfindingTb4b4b4,
Te6edf3rotate_session_csrf_tokenTb4b4b4,
Te6edf3rrc_protocolTb4b4b4,
Te6edf3safe_path_under_dirTb4b4b4,
Te6edf3sanitize_sticker_emojiTb4b4b4,
Te6edf3sanitize_sticker_nameTb4b4b4,
Te6edf3sanitize_websocket_config_updateTb4b4b4,
Te6edf3save_app_security_settingsTb4b4b4,
Te6edf3secretsTb4b4b4,
Te6edf3shutilTb4b4b4,
Te6edf3sqlite3Tb4b4b4,
Te6edf3sticker_pack_utilsTb4b4b4,
Te6edf3sysTb4b4b4,
Te6edf3tempfileTb4b4b4,
Te6edf3threadingTb4b4b4,
Te6edf3timeTb4b4b4,
Te6edf3tracebackTb4b4b4,
Te6edf3user_agent_hashTb4b4b4,
Te6edf3validate_export_documentTb4b4b4,
Te6edf3webTb4b4b4,
Te6edf3websocket_type_requires_authTb4b4b4,
Te6edf3zipfileTb4b4b4,
Tb4b4b4)


Tff7b72def Td2a8ffregister_lxmf_routesTb4b4b4(Te6edf3routesTb4b4b4, Te6edf3appTb4b4b4)Tb4b4b4:

Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/propagation-node/statusTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffpropagation_node_statusTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3router Tff7b72= Te6edf3appTff7b72.Td2a8ffmessage_router
Te6edf3current_state Tff7b72= Tff7b72None
Te6edf3current_progress Tff7b72= T79c0ff0.0
Te6edf3transfer_size Tff7b72= Tff7b72None
Tff7b72if Te6edf3router Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3current_state Tff7b72= Te6edf3routerTff7b72.Td2a8ffpropagation_transfer_state
Te6edf3current_progress Tff7b72= Te6edf3routerTff7b72.Td2a8ffpropagation_transfer_progress
Te6edf3transfer_size Tff7b72= Tffa657getattrTb4b4b4(
Te6edf3routerTb4b4b4,
Ta5d6ff"Ta5d6ffpropagation_transfer_sizeTa5d6ff"Tb4b4b4,
Tff7b72NoneTb4b4b4,
Tb4b4b4)
T8b949e# COMPLETE is terminal, so reset to idle so the UI does not keep
T8b949e# looking "busy" after a finished auto/manual sync.
Tff7b72if Te6edf3current_state Tff7b72== Te6edf3routerTff7b72.Td2a8ffPR_COMPLETETb4b4b4:
Tff7b72with Te6edf3contextlibTff7b72.Td2a8ffsuppressTb4b4b4(Tf85149ExceptionTb4b4b4)Tb4b4b4:
Te6edf3routerTff7b72.Td2a8ffpropagation_transfer_state Tff7b72= Te6edf3routerTff7b72.Td2a8ffPR_IDLE
Te6edf3routerTff7b72.Td2a8ffpropagation_transfer_progress Tff7b72= T79c0ff0.0
Tff7b72if Tffa657hasattrTb4b4b4(Te6edf3routerTb4b4b4, Ta5d6ff"Ta5d6ffpropagation_transfer_sizeTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3routerTff7b72.Td2a8ffpropagation_transfer_size Tff7b72= Tff7b72None
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Te6edf3sync_metrics Tff7b72= Te6edf3appTff7b72.Td2a8ff_collect_propagation_sync_metricsTb4b4b4(Tb4b4b4)
Te6edf3progress_raw Tff7b72= Te6edf3current_progress
Tff7b72tryTb4b4b4:
Te6edf3progress_pct Tff7b72= Tffa657floatTb4b4b4(Te6edf3progress_rawTb4b4b4) Tff7b72* T79c0ff100
Tff7b72except Tb4b4b4(Tf85149TypeErrorTb4b4b4, Tf85149ValueErrorTb4b4b4)Tb4b4b4:
Te6edf3progress_pct Tff7b72= T79c0ff0.0
Te6edf3last_result Tff7b72= Tffa657getattrTb4b4b4(
Te6edf3appTff7b72.Td2a8ffmessage_routerTb4b4b4,
Ta5d6ff"Ta5d6ffpropagation_transfer_last_resultTa5d6ff"Tb4b4b4,
Tff7b72NoneTb4b4b4,
Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3last_resultTb4b4b4, Tb4b4b4(Tffa657intTb4b4b4, Tffa657floatTb4b4b4, Tffa657strTb4b4b4, Tffa657typeTb4b4b4(Tff7b72NoneTb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4:
Te6edf3last_result Tff7b72= Tff7b72None
Tff7b72if Te6edf3current_state Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3current_state Tff7b72= T79c0ff0
Te6edf3transfer_size_bytes Tff7b72= Tff7b72None
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3transfer_sizeTb4b4b4, Tb4b4b4(Tffa657intTb4b4b4, Tffa657floatTb4b4b4)Tb4b4b4) Tff7b72and Te6edf3transfer_size Tff7b72> T79c0ff0Tb4b4b4:
Te6edf3transfer_size_bytes Tff7b72= Tffa657intTb4b4b4(Te6edf3transfer_sizeTb4b4b4)
Te6edf3inbound_delivery_count Tff7b72= T79c0ff0
Te6edf3inbound_deliveries Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72if Te6edf3router Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Te6edf3inbound_deliveries Tff7b72= Te6edf3list_inbound_deliveriesTb4b4b4(Te6edf3routerTb4b4b4)
Te6edf3inbound_delivery_count Tff7b72= Tffa657lenTb4b4b4(Te6edf3inbound_deliveriesTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffpropagation_node_statusTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstateTa5d6ff"Tb4b4b4: Te6edf3convert_propagation_node_state_to_stringTb4b4b4(
Te6edf3current_stateTb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4: Te6edf3progress_pctTb4b4b4,
Ta5d6ff"Ta5d6fftransfer_size_bytesTa5d6ff"Tb4b4b4: Te6edf3transfer_size_bytesTb4b4b4,
Ta5d6ff"Ta5d6ffinbound_delivery_countTa5d6ff"Tb4b4b4: Te6edf3inbound_delivery_countTb4b4b4,
Ta5d6ff"Ta5d6ffinbound_deliveriesTa5d6ff"Tb4b4b4: Te6edf3inbound_deliveriesTb4b4b4,
Ta5d6ff"Ta5d6ffmessages_receivedTa5d6ff"Tb4b4b4: Te6edf3last_resultTb4b4b4,
Ta5d6ff"Ta5d6ffmessages_storedTa5d6ff"Tb4b4b4: Te6edf3sync_metricsTb4b4b4[Ta5d6ff"Ta5d6ffmessages_storedTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffdelivery_confirmationsTa5d6ff"Tb4b4b4: Te6edf3sync_metricsTb4b4b4[Ta5d6ff"Ta5d6ffdelivery_confirmationsTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffmessages_hiddenTa5d6ff"Tb4b4b4: Te6edf3sync_metricsTb4b4b4[Ta5d6ff"Ta5d6ffmessages_hiddenTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6fflocal_propagation_nodeTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_local_propagation_node_statsTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# sync propagation node

T8b949e# sync propagation node
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/propagation-node/syncTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffpropagation_node_syncTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# ensure propagation node is configured before attempting to sync
Te6edf3outbound_node Tff7b72= Te6edf3appTff7b72.Td2a8ffmessage_routerTff7b72.Td2a8ffget_outbound_propagation_nodeTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3outbound_node Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffA propagation node must be configured to sync messages.Ta5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)

T8b949e# proactively request path, but do not block/fail here.
T8b949e# LXMF internally manages PR_PATH_REQUESTED and retries.
Tff7b72if Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3outbound_nodeTb4b4b4)Tb4b4b4:
Tff7b72with Te6edf3contextlibTff7b72.Td2a8ffsuppressTb4b4b4(Tf85149ExceptionTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3outbound_nodeTb4b4b4)

T8b949e# request messages from propagation node
Tff7b72await Te6edf3appTff7b72.Td2a8ffsync_propagation_nodesTb4b4b4(Te6edf3forceTff7b72=Tff7b72TrueTb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffSync is startingTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# stop syncing propagation node

T8b949e# stop syncing propagation node
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/propagation-node/stop-syncTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffpropagation_node_stop_syncTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3appTff7b72.Td2a8ffstop_propagation_node_syncTb4b4b4(Tb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffSync is stoppingTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/propagation-node/cancel-inboundTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffpropagation_node_cancel_inboundTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3router Tff7b72= Te6edf3appTff7b72.Td2a8ffmessage_router
Te6edf3data Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72with Te6edf3contextlibTff7b72.Td2a8ffsuppressTb4b4b4(Tf85149ExceptionTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3dataTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tb4b4b4{Tb4b4b4}
Te6edf3resource_hash Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffresource_hashTa5d6ff"Tb4b4b4)
Te6edf3result Tff7b72= Te6edf3cancel_inbound_deliveriesTb4b4b4(Te6edf3routerTb4b4b4, Te6edf3resource_hashTff7b72=Te6edf3resource_hashTb4b4b4)
Tff7b72if Tff7b72not Te6edf3resultTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3status Tff7b72= T79c0ff503 Tff7b72if Ta5d6ff"Ta5d6ffunavailableTa5d6ff" Tff7b72in Tffa657strTb4b4b4(Te6edf3resultTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4) Tff7b72else T79c0ff400
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3resultTff7b72.Td2a8ffgetTb4b4b4(
Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffFailed to cancel inbound deliveriesTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffcancelledTa5d6ff"Tb4b4b4: Te6edf3resultTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcancelledTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=Te6edf3statusTb4b4b4,
Tb4b4b4)
Te6edf3cancelled Tff7b72= Tffa657intTb4b4b4(Te6edf3resultTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcancelledTa5d6ff"Tb4b4b4) Tff7b72or T79c0ff0Tb4b4b4)
Tff7b72if Te6edf3resource_hashTb4b4b4:
Te6edf3message Tff7b72= Tb4b4b4(
Ta5d6ff"Ta5d6ffCancelled inbound deliveryTa5d6ff"
Tff7b72if Te6edf3cancelled
Tff7b72else Ta5d6ff"Ta5d6ffInbound delivery was not activeTa5d6ff"
Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3message Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffCancelled Tffd700{Te6edf3cancelledTffd700}Ta5d6ff inbound deliveriesTa5d6ff"
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3messageTb4b4b4,
Ta5d6ff"Ta5d6ffcancelledTa5d6ff"Tb4b4b4: Te6edf3cancelledTb4b4b4,
Ta5d6ff"Ta5d6ffresource_hashTa5d6ff"Tb4b4b4: Te6edf3resultTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffresource_hashTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffinbound_delivery_countTa5d6ff"Tb4b4b4: Tffa657lenTb4b4b4(Te6edf3list_inbound_deliveriesTb4b4b4(Te6edf3routerTb4b4b4)Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffinbound_deliveriesTa5d6ff"Tb4b4b4: Te6edf3list_inbound_deliveriesTb4b4b4(Te6edf3routerTb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/propagation-node/stopTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffpropagation_node_stopTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3appTff7b72.Td2a8ffconfigTff7b72.Td2a8fflxmf_local_propagation_node_enabledTff7b72.Td2a8ffsetTb4b4b4(Tff7b72FalseTb4b4b4)
Te6edf3appTff7b72.Td2a8ffstop_local_propagation_nodeTb4b4b4(Tb4b4b4)
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(Te6edf3appTff7b72.Td2a8ffsend_config_to_websocket_clientsTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffLocal propagation node stoppedTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflocal_propagation_nodeTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_local_propagation_node_statsTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/propagation-node/restartTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffpropagation_node_restartTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3appTff7b72.Td2a8ffconfigTff7b72.Td2a8fflxmf_local_propagation_node_enabledTff7b72.Td2a8ffsetTb4b4b4(Tff7b72TrueTb4b4b4)
Te6edf3appTff7b72.Td2a8ffrestart_local_propagation_nodeTb4b4b4(Tb4b4b4)
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(Te6edf3appTff7b72.Td2a8ffsend_config_to_websocket_clientsTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffLocal propagation node restartedTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflocal_propagation_nodeTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_local_propagation_node_statsTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# serve propagation nodes

T8b949e# serve propagation nodes
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/propagation-nodesTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffpropagation_nodes_getTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3ctx Tff7b72= Te6edf3appTff7b72.Td2a8ffcurrent_context
Tff7b72if Tff7b72not Te6edf3ctx Tff7b72or Tff7b72not Tffa657getattrTb4b4b4(Te6edf3ctxTb4b4b4, Ta5d6ff"Ta5d6ffrunningTa5d6ff"Tb4b4b4, Tff7b72FalseTb4b4b4) Tff7b72or Te6edf3ctxTff7b72.Td2a8ffdatabase Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffApplication is initializing or switching identityTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff503Tb4b4b4,
Tb4b4b4)
Te6edf3database Tff7b72= Te6edf3ctxTff7b72.Td2a8ffdatabase
T8b949e# get query params
Te6edf3limit Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflimitTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)

T8b949e# get lxmf.propagation announces
Te6edf3results Tff7b72= Te6edf3databaseTff7b72.Td2a8ffannouncesTff7b72.Td2a8ffget_announcesTb4b4b4(Te6edf3aspectTff7b72=Ta5d6ff"Ta5d6fflxmf.propagationTa5d6ff"Tb4b4b4)

T8b949e# limit results
Tff7b72if Te6edf3limit Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3results Tff7b72= Te6edf3resultsTb4b4b4[Tb4b4b4: Tffa657intTb4b4b4(Te6edf3limitTb4b4b4)Tb4b4b4]
Tff7b72except Tb4b4b4(Tf85149ValueErrorTb4b4b4, Tf85149TypeErrorTb4b4b4)Tb4b4b4:
Tff7b72pass

T8b949e# process announces
Te6edf3lxmf_propagation_nodes Tff7b72= Tb4b4b4[Tb4b4b4]
Te6edf3local_identity_hash Tff7b72= Te6edf3ctxTff7b72.Td2a8ffidentityTff7b72.Td2a8ffhashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4) Tff7b72if Te6edf3ctx Tff7b72else Tff7b72None
Te6edf3local_destination_hash_raw Tff7b72= Tb4b4b4(
Tffa657getattrTb4b4b4(Te6edf3ctxTff7b72.Td2a8ffmessage_routerTff7b72.Td2a8ffpropagation_destinationTb4b4b4, Ta5d6ff"Ta5d6ffhexhashTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Tff7b72if Te6edf3ctx
Tff7b72else Tff7b72None
Tb4b4b4)
Tff7b72if Te6edf3local_destination_hash_raw Tff7b72is Tff7b72None Tff7b72and Te6edf3ctxTb4b4b4:
Te6edf3local_destination_hash_raw Tff7b72= Tffa657getattrTb4b4b4(
Te6edf3ctxTff7b72.Td2a8ffmessage_routerTff7b72.Td2a8ffpropagation_destinationTb4b4b4,
Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4,
Tff7b72NoneTb4b4b4,
Tb4b4b4)
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3local_destination_hash_rawTb4b4b4, Tffa657bytesTb4b4b4)Tb4b4b4:
Te6edf3local_destination_hash Tff7b72= Te6edf3local_destination_hash_rawTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)
Tff7b72elif Tffa657isinstanceTb4b4b4(Te6edf3local_destination_hash_rawTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3local_destination_hash Tff7b72= Te6edf3local_destination_hash_raw
Tff7b72elseTb4b4b4:
Te6edf3local_destination_hash Tff7b72= Tff7b72None
Te6edf3local_stats Tff7b72= Te6edf3appTff7b72.Td2a8ffget_local_propagation_node_statsTb4b4b4(Te6edf3contextTff7b72=Te6edf3ctxTb4b4b4) Tff7b72if Te6edf3ctx Tff7b72else Tff7b72None
Tff7b72for Te6edf3announce Tff7b72in Te6edf3resultsTb4b4b4:
T8b949e# find an lxmf.delivery announce for the same identity hash, so we can use that as an "operater by" name
Te6edf3lxmf_delivery_results Tff7b72= Te6edf3databaseTff7b72.Td2a8ffannouncesTff7b72.Td2a8ffget_filtered_announcesTb4b4b4(
Te6edf3aspectTff7b72=Ta5d6ff"Ta5d6fflxmf.deliveryTa5d6ff"Tb4b4b4,
Te6edf3identity_hashTff7b72=Te6edf3announceTb4b4b4[Ta5d6ff"Ta5d6ffidentity_hashTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4)
Te6edf3lxmf_delivery_announce Tff7b72= Tb4b4b4(
Te6edf3lxmf_delivery_resultsTb4b4b4[T79c0ff0Tb4b4b4] Tff7b72if Te6edf3lxmf_delivery_results Tff7b72else Tff7b72None
Tb4b4b4)

T8b949e# find a nomadnetwork.node announce for the same identity hash, so we can use that as an "operated by" name
Te6edf3nomadnetwork_node_results Tff7b72= Te6edf3databaseTff7b72.Td2a8ffannouncesTff7b72.Td2a8ffget_filtered_announcesTb4b4b4(
Te6edf3aspectTff7b72=Ta5d6ff"Ta5d6ffnomadnetwork.nodeTa5d6ff"Tb4b4b4,
Te6edf3identity_hashTff7b72=Te6edf3announceTb4b4b4[Ta5d6ff"Ta5d6ffidentity_hashTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4)
Te6edf3nomadnetwork_node_announce Tff7b72= Tb4b4b4(
Te6edf3nomadnetwork_node_resultsTb4b4b4[T79c0ff0Tb4b4b4] Tff7b72if Te6edf3nomadnetwork_node_results Tff7b72else Tff7b72None
Tb4b4b4)

T8b949e# get a display name from other announces belonging to the propagation nodes identity
Te6edf3operator_display_name Tff7b72= Tff7b72None
Tff7b72if Tb4b4b4(
Te6edf3lxmf_delivery_announce Tff7b72is Tff7b72not Tff7b72None
Tff7b72and Te6edf3lxmf_delivery_announceTb4b4b4[Ta5d6ff"Ta5d6ffapp_dataTa5d6ff"Tb4b4b4] Tff7b72is Tff7b72not Tff7b72None
Tb4b4b4)Tb4b4b4:
Te6edf3operator_display_name Tff7b72= Te6edf3parse_lxmf_display_nameTb4b4b4(
Te6edf3lxmf_delivery_announceTb4b4b4[Ta5d6ff"Ta5d6ffapp_dataTa5d6ff"Tb4b4b4]Tb4b4b4,
Tff7b72NoneTb4b4b4,
Tb4b4b4)
Tff7b72elif Tb4b4b4(
Te6edf3nomadnetwork_node_announce Tff7b72is Tff7b72not Tff7b72None
Tff7b72and Te6edf3nomadnetwork_node_announceTb4b4b4[Ta5d6ff"Ta5d6ffapp_dataTa5d6ff"Tb4b4b4] Tff7b72is Tff7b72not Tff7b72None
Tb4b4b4)Tb4b4b4:
Te6edf3operator_display_name Tff7b72= Te6edf3parse_nomadnetwork_node_display_nameTb4b4b4(
Te6edf3nomadnetwork_node_announceTb4b4b4[Ta5d6ff"Ta5d6ffapp_dataTa5d6ff"Tb4b4b4]Tb4b4b4,
Tff7b72NoneTb4b4b4,
Tb4b4b4)

T8b949e# parse app_data so we can see if propagation is enabled or disabled for this node
Te6edf3is_propagation_enabled Tff7b72= Tff7b72None
Te6edf3per_transfer_limit Tff7b72= Tff7b72None
Te6edf3propagation_node_data Tff7b72= Te6edf3parse_lxmf_propagation_node_app_dataTb4b4b4(
Te6edf3announceTb4b4b4[Ta5d6ff"Ta5d6ffapp_dataTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3propagation_node_data Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Te6edf3is_propagation_enabled Tff7b72= Te6edf3propagation_node_dataTb4b4b4[Ta5d6ff"Ta5d6ffenabledTa5d6ff"Tb4b4b4]
Te6edf3per_transfer_limit Tff7b72= Te6edf3propagation_node_dataTb4b4b4[Ta5d6ff"Ta5d6ffper_transfer_limitTa5d6ff"Tb4b4b4]

T8b949e# ensure created_at and updated_at have Z suffix for UTC if they don't have a timezone
Te6edf3created_at Tff7b72= Tffa657strTb4b4b4(Te6edf3announceTb4b4b4[Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72if Te6edf3created_at Tff7b72and Ta5d6ff"Ta5d6ff+Ta5d6ff" Tff7b72not Tff7b72in Te6edf3created_at Tff7b72and Ta5d6ff"Ta5d6ffZTa5d6ff" Tff7b72not Tff7b72in Te6edf3created_atTb4b4b4:
Te6edf3created_at Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ffZTa5d6ff"

Te6edf3updated_at Tff7b72= Tffa657strTb4b4b4(Te6edf3announceTb4b4b4[Ta5d6ff"Ta5d6ffupdated_atTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72if Te6edf3updated_at Tff7b72and Ta5d6ff"Ta5d6ff+Ta5d6ff" Tff7b72not Tff7b72in Te6edf3updated_at Tff7b72and Ta5d6ff"Ta5d6ffZTa5d6ff" Tff7b72not Tff7b72in Te6edf3updated_atTb4b4b4:
Te6edf3updated_at Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ffZTa5d6ff"

Te6edf3is_local_node Tff7b72= Tb4b4b4(
Te6edf3announceTb4b4b4[Ta5d6ff"Ta5d6ffidentity_hashTa5d6ff"Tb4b4b4] Tff7b72== Te6edf3local_identity_hash
Tff7b72or Te6edf3announceTb4b4b4[Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4] Tff7b72== Te6edf3local_destination_hash
Tb4b4b4)
Tff7b72if Te6edf3is_local_node Tff7b72and Tffa657isinstanceTb4b4b4(Te6edf3local_statsTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3local_running Tff7b72= Te6edf3local_statsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffis_runningTa5d6ff"Tb4b4b4)
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3local_runningTb4b4b4, Tffa657boolTb4b4b4)Tb4b4b4:
Te6edf3is_propagation_enabled Tff7b72= Te6edf3local_running

Te6edf3lxmf_propagation_nodesTff7b72.Td2a8ffappendTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3announceTb4b4b4[Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffidentity_hashTa5d6ff"Tb4b4b4: Te6edf3announceTb4b4b4[Ta5d6ff"Ta5d6ffidentity_hashTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffoperator_display_nameTa5d6ff"Tb4b4b4: Te6edf3operator_display_nameTb4b4b4,
Ta5d6ff"Ta5d6ffis_propagation_enabledTa5d6ff"Tb4b4b4: Te6edf3is_propagation_enabledTb4b4b4,
Ta5d6ff"Ta5d6ffper_transfer_limitTa5d6ff"Tb4b4b4: Te6edf3per_transfer_limitTb4b4b4,
Ta5d6ff"Ta5d6ffis_local_nodeTa5d6ff"Tb4b4b4: Te6edf3is_local_nodeTb4b4b4,
Ta5d6ff"Ta5d6fflocal_node_statsTa5d6ff"Tb4b4b4: Tb4b4b4(Te6edf3local_stats Tff7b72if Te6edf3is_local_node Tff7b72else Tff7b72NoneTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4: Te6edf3created_atTb4b4b4,
Ta5d6ff"Ta5d6ffupdated_atTa5d6ff"Tb4b4b4: Te6edf3updated_atTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tff7b72if Tb4b4b4(
Te6edf3ctx Tff7b72is Tff7b72not Tff7b72None
Tff7b72and Te6edf3local_destination_hash Tff7b72is Tff7b72not Tff7b72None
Tff7b72and Tff7b72not Tffa657anyTb4b4b4(
Te6edf3nodeTb4b4b4[Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4] Tff7b72== Te6edf3local_destination_hash
Tff7b72for Te6edf3node Tff7b72in Te6edf3lxmf_propagation_nodes
Tb4b4b4)
Tb4b4b4)Tb4b4b4:
Te6edf3now_iso Tff7b72= Te6edf3datetimeTff7b72.Td2a8ffnowTb4b4b4(Te6edf3UTCTb4b4b4)Tff7b72.Td2a8ffisoformatTb4b4b4(Tb4b4b4)
Te6edf3lxmf_propagation_nodesTff7b72.Td2a8ffinsertTb4b4b4(
T79c0ff0Tb4b4b4,
Tb4b4b4{
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3local_destination_hashTb4b4b4,
Ta5d6ff"Ta5d6ffidentity_hashTa5d6ff"Tb4b4b4: Te6edf3local_identity_hashTb4b4b4,
Ta5d6ff"Ta5d6ffoperator_display_nameTa5d6ff"Tb4b4b4: Te6edf3ctxTff7b72.Td2a8ffconfigTff7b72.Td2a8ffdisplay_nameTff7b72.Td2a8ffgetTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffis_propagation_enabledTa5d6ff"Tb4b4b4: Tb4b4b4(
Te6edf3local_statsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffis_runningTa5d6ff"Tb4b4b4)
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3local_statsTb4b4b4, Tffa657dictTb4b4b4)
Tff7b72and Tffa657isinstanceTb4b4b4(Te6edf3local_statsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffis_runningTa5d6ff"Tb4b4b4)Tb4b4b4, Tffa657boolTb4b4b4)
Tff7b72else Te6edf3ctxTff7b72.Td2a8ffconfigTff7b72.Td2a8fflxmf_local_propagation_node_enabledTff7b72.Td2a8ffgetTb4b4b4(Tb4b4b4)
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffper_transfer_limitTa5d6ff"Tb4b4b4: Tffa657intTb4b4b4(
Tffa657getattrTb4b4b4(
Te6edf3ctxTff7b72.Td2a8ffmessage_routerTb4b4b4,
Ta5d6ff"Ta5d6ffpropagation_per_transfer_limitTa5d6ff"Tb4b4b4,
T79c0ff0Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffis_local_nodeTa5d6ff"Tb4b4b4: Tff7b72TrueTb4b4b4,
Ta5d6ff"Ta5d6fflocal_node_statsTa5d6ff"Tb4b4b4: Te6edf3local_statsTb4b4b4,
Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4: Te6edf3now_isoTb4b4b4,
Ta5d6ff"Ta5d6ffupdated_atTa5d6ff"Tb4b4b4: Te6edf3now_isoTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fflxmf_propagation_nodesTa5d6ff"Tb4b4b4: Te6edf3lxmf_propagation_nodesTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# get path to destination
Tff7b72async Tff7b72def Td2a8ffpath_tableTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3limit Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflimitTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Te6edf3offset Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffoffsetTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Te6edf3destination_hashes Tff7b72= Tff7b72None
Tff7b72if Te6edf3requestTff7b72.Td2a8ffmethod Tff7b72== Ta5d6ff"Ta5d6ffPOSTTa5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3body Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3destination_hashes Tff7b72= Te6edf3bodyTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashesTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3destination_hashes Tff7b72and Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3destination_hashesTb4b4b4, Tffa657listTb4b4b4)Tb4b4b4:
Te6edf3destination_hashes Tff7b72= Tff7b72None
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass

Te6edf3all_paths Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72if Tffa657hasattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffreticulumTa5d6ff"Tb4b4b4) Tff7b72and Te6edf3appTff7b72.Td2a8ffreticulumTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3all_paths Tff7b72= Te6edf3appTff7b72.Td2a8ffreticulumTff7b72.Td2a8ffget_path_tableTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass

Tff7b72if Te6edf3destination_hashesTb4b4b4:
Te6edf3hash_set Tff7b72= Tb4b4b4{Te6edf3hTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72for Te6edf3h Tff7b72in Te6edf3destination_hashes Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3hTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4}
Te6edf3all_paths Tff7b72= Tb4b4b4[Te6edf3p Tff7b72for Te6edf3p Tff7b72in Te6edf3all_paths Tff7b72if Te6edf3pTb4b4b4[Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4]Tff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72in Te6edf3hash_setTb4b4b4]

Te6edf3total_count Tff7b72= Tffa657lenTb4b4b4(Te6edf3all_pathsTb4b4b4)

T8b949e# apply pagination if requested
Tff7b72if Te6edf3limit Tff7b72is Tff7b72not Tff7b72None Tff7b72or Te6edf3offset Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3start Tff7b72= Tffa657intTb4b4b4(Te6edf3offsetTb4b4b4) Tff7b72if Te6edf3offset Tff7b72else T79c0ff0
Te6edf3end Tff7b72= Tb4b4b4(Te6edf3start Tff7b72+ Tffa657intTb4b4b4(Te6edf3limitTb4b4b4)Tb4b4b4) Tff7b72if Te6edf3limit Tff7b72else Te6edf3total_count
Te6edf3paginated_paths Tff7b72= Te6edf3all_pathsTb4b4b4[Te6edf3startTb4b4b4:Te6edf3endTb4b4b4]
Tff7b72except Tb4b4b4(Tf85149ValueErrorTb4b4b4, Tf85149TypeErrorTb4b4b4)Tb4b4b4:
Te6edf3paginated_paths Tff7b72= Te6edf3all_paths
Tff7b72elseTb4b4b4:
Te6edf3paginated_paths Tff7b72= Te6edf3all_paths

Te6edf3path_table Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3path Tff7b72in Te6edf3paginated_pathsTb4b4b4:
Te6edf3pathTb4b4b4[Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3pathTb4b4b4[Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4]Tff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)
Te6edf3pathTb4b4b4[Ta5d6ff"Ta5d6ffviaTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3pathTb4b4b4[Ta5d6ff"Ta5d6ffviaTa5d6ff"Tb4b4b4]Tff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)
Te6edf3path_tableTff7b72.Td2a8ffappendTb4b4b4(Te6edf3pathTb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffpath_tableTa5d6ff"Tb4b4b4: Te6edf3path_tableTb4b4b4,
Ta5d6ff"Ta5d6fftotal_countTa5d6ff"Tb4b4b4: Te6edf3total_countTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# send lxmf message
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf-messages/sendTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_messages_sendTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787demo_mode Tff7b72import Te6edf3demo_mode_block_response

Te6edf3blocked Tff7b72= Te6edf3demo_mode_block_responseTb4b4b4(Te6edf3appTb4b4b4)
Tff7b72if Te6edf3blocked Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3blocked
T8b949e# get request body as json
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)

Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3dataTb4b4b4, Tffa657dictTb4b4b4) Tff7b72or Ta5d6ff"Ta5d6fflxmf_messageTa5d6ff" Tff7b72not Tff7b72in Te6edf3dataTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fflxmf_message is requiredTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3lm Tff7b72= Te6edf3dataTb4b4b4[Ta5d6ff"Ta5d6fflxmf_messageTa5d6ff"Tb4b4b4]
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3lmTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fflxmf_message must be an objectTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)

T8b949e# get delivery method
Te6edf3delivery_method Tff7b72= Tff7b72None
Tff7b72if Ta5d6ff"Ta5d6ffdelivery_methodTa5d6ff" Tff7b72in Te6edf3dataTb4b4b4:
Te6edf3delivery_method Tff7b72= Te6edf3dataTb4b4b4[Ta5d6ff"Ta5d6ffdelivery_methodTa5d6ff"Tb4b4b4]

Tff7b72tryTb4b4b4:
Te6edf3destination_hash Tff7b72= Te6edf3lmTb4b4b4[Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4]
Te6edf3content Tff7b72= Te6edf3lmTb4b4b4[Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4]
Tff7b72except Tb4b4b4(Tf85149KeyErrorTb4b4b4, Tf85149TypeErrorTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdestination_hash and content are requiredTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)

Te6edf3raw_fields Tff7b72= Te6edf3lmTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4)
Te6edf3fields Tff7b72= Tffa657dictTb4b4b4(Te6edf3raw_fieldsTb4b4b4) Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3raw_fieldsTb4b4b4, Tffa657dictTb4b4b4) Tff7b72else Tb4b4b4{Tb4b4b4}
Te6edf3app_extensions_payload Tff7b72= Te6edf3fieldsTff7b72.Td2a8ffpopTb4b4b4(Ta5d6ff"Ta5d6ffapp_extensionsTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Te6edf3validated_app_extensions Tff7b72= Tb4b4b4(
Te6edf3app_extensions_payload Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3app_extensions_payloadTb4b4b4, Tffa657dictTb4b4b4) Tff7b72else Tff7b72None
Tb4b4b4)

Te6edf3image_field Tff7b72= Tff7b72None
Te6edf3audio_field Tff7b72= Tff7b72None
Te6edf3file_attachments_field Tff7b72= Tff7b72None
Te6edf3telemetry_data Tff7b72= Tff7b72None
Te6edf3commands Tff7b72= Tff7b72None

Tff7b72tryTb4b4b4:
Tff7b72if Ta5d6ff"Ta5d6ffimageTa5d6ff" Tff7b72in Te6edf3fields Tff7b72and Tffa657isinstanceTb4b4b4(Te6edf3fieldsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffimageTa5d6ff"Tb4b4b4)Tb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3image_bytes Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(Te6edf3fieldsTb4b4b4[Ta5d6ff"Ta5d6ffimageTa5d6ff"Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffimage_bytesTa5d6ff"Tb4b4b4]Tb4b4b4)
Te6edf3detected Tff7b72= Te6edf3detect_image_format_from_magicTb4b4b4(Te6edf3image_bytesTb4b4b4)
Tff7b72if Te6edf3detected Tff7b72is Tff7b72None Tff7b72or Te6edf3detected Tff7b72in Tb4b4b4{Ta5d6ff"Ta5d6ffwebmTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fftgsTa5d6ff"Tb4b4b4}Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid image attachmentTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3image_type Tff7b72= Ta5d6ff"Ta5d6ffjpgTa5d6ff" Tff7b72if Te6edf3detected Tff7b72== Ta5d6ff"Ta5d6ffjpegTa5d6ff" Tff7b72else Te6edf3detected
Te6edf3image_field Tff7b72= Te6edf3LxmfImageFieldTb4b4b4(Te6edf3image_typeTb4b4b4, Te6edf3image_bytesTb4b4b4)

Tff7b72if Ta5d6ff"Ta5d6ffaudioTa5d6ff" Tff7b72in Te6edf3fields Tff7b72and Tffa657isinstanceTb4b4b4(Te6edf3fieldsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffaudioTa5d6ff"Tb4b4b4)Tb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3audio_mode Tff7b72= Te6edf3fieldsTb4b4b4[Ta5d6ff"Ta5d6ffaudioTa5d6ff"Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffaudio_modeTa5d6ff"Tb4b4b4]
Te6edf3audio_bytes Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(Te6edf3fieldsTb4b4b4[Ta5d6ff"Ta5d6ffaudioTa5d6ff"Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffaudio_bytesTa5d6ff"Tb4b4b4]Tb4b4b4)
Te6edf3audio_field Tff7b72= Te6edf3LxmfAudioFieldTb4b4b4(Te6edf3audio_modeTb4b4b4, Te6edf3audio_bytesTb4b4b4)

Tff7b72if Ta5d6ff"Ta5d6fffile_attachmentsTa5d6ff" Tff7b72in Te6edf3fields Tff7b72and Tffa657isinstanceTb4b4b4(
Te6edf3fieldsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffile_attachmentsTa5d6ff"Tb4b4b4)Tb4b4b4,
Tffa657listTb4b4b4,
Tb4b4b4)Tb4b4b4:
Te6edf3file_attachments Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3file_attachment Tff7b72in Te6edf3fieldsTb4b4b4[Ta5d6ff"Ta5d6fffile_attachmentsTa5d6ff"Tb4b4b4]Tb4b4b4:
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3file_attachmentTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3file_name Tff7b72= Te6edf3file_attachmentTb4b4b4[Ta5d6ff"Ta5d6fffile_nameTa5d6ff"Tb4b4b4]
Te6edf3file_bytes Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(Te6edf3file_attachmentTb4b4b4[Ta5d6ff"Ta5d6fffile_bytesTa5d6ff"Tb4b4b4]Tb4b4b4)
Te6edf3file_attachmentsTff7b72.Td2a8ffappendTb4b4b4(
Te6edf3LxmfFileAttachmentTb4b4b4(Te6edf3file_nameTb4b4b4, Te6edf3file_bytesTb4b4b4)Tb4b4b4,
Tb4b4b4)

Te6edf3file_attachments_field Tff7b72= Te6edf3LxmfFileAttachmentsFieldTb4b4b4(Te6edf3file_attachmentsTb4b4b4)

Tff7b72if Ta5d6ff"Ta5d6fftelemetryTa5d6ff" Tff7b72in Te6edf3fieldsTb4b4b4:
Te6edf3telemetry_val Tff7b72= Te6edf3fieldsTb4b4b4[Ta5d6ff"Ta5d6fftelemetryTa5d6ff"Tb4b4b4]
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3telemetry_valTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3telemetry_data Tff7b72= Te6edf3TelemeterTff7b72.Td2a8ffpackTb4b4b4(Te6edf3locationTff7b72=Te6edf3telemetry_valTb4b4b4)
Tff7b72elif Tffa657isinstanceTb4b4b4(Te6edf3telemetry_valTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3telemetry_data Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(Te6edf3telemetry_valTb4b4b4)

Tff7b72if Ta5d6ff"Ta5d6ffcommandsTa5d6ff" Tff7b72in Te6edf3fields Tff7b72and Tffa657isinstanceTb4b4b4(Te6edf3fieldsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcommandsTa5d6ff"Tb4b4b4)Tb4b4b4, Tffa657listTb4b4b4)Tb4b4b4:
Te6edf3commands Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3cmd Tff7b72in Te6edf3fieldsTb4b4b4[Ta5d6ff"Ta5d6ffcommandsTa5d6ff"Tb4b4b4]Tb4b4b4:
Te6edf3new_cmd Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3cmdTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72for Te6edf3kTb4b4b4, Te6edf3v Tff7b72in Te6edf3cmdTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3kTff7b72.Td2a8ffstartswithTb4b4b4(Ta5d6ff"Ta5d6ff0xTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3new_cmdTb4b4b4[Tffa657intTb4b4b4(Te6edf3kTb4b4b4, T79c0ff16Tb4b4b4)Tb4b4b4] Tff7b72= Te6edf3v
Tff7b72elseTb4b4b4:
Te6edf3new_cmdTb4b4b4[Tffa657intTb4b4b4(Te6edf3kTb4b4b4)Tb4b4b4] Tff7b72= Te6edf3v
Tff7b72except Tb4b4b4(Tf85149ValueErrorTb4b4b4, Tf85149TypeErrorTb4b4b4)Tb4b4b4:
Te6edf3new_cmdTb4b4b4[Te6edf3kTb4b4b4] Tff7b72= Te6edf3v
Te6edf3commandsTff7b72.Td2a8ffappendTb4b4b4(Te6edf3new_cmdTb4b4b4)
Tff7b72except Tb4b4b4(Tf85149KeyErrorTb4b4b4, Tf85149TypeErrorTb4b4b4, Tf85149ValueErrorTb4b4b4, Te6edf3binasciiTff7b72.Td2a8ffErrorTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid lxmf_message.fieldsTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)

Te6edf3reply_to_hash Tff7b72= Tff7b72None
Tff7b72if Ta5d6ff"Ta5d6ffreply_to_hashTa5d6ff" Tff7b72in Te6edf3lmTb4b4b4:
Te6edf3reply_to_hash Tff7b72= Te6edf3lmTb4b4b4[Ta5d6ff"Ta5d6ffreply_to_hashTa5d6ff"Tb4b4b4]
Te6edf3reply_quoted_content Tff7b72= Te6edf3lmTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffreply_quoted_contentTa5d6ff"Tb4b4b4) Tff7b72or Tff7b72None

Tff7b72tryTb4b4b4:
T8b949e# send lxmf message to destination
Te6edf3lxmf_message Tff7b72= Tff7b72await Te6edf3appTff7b72.Td2a8ffsend_messageTb4b4b4(
Te6edf3destination_hashTff7b72=Te6edf3destination_hashTb4b4b4,
Te6edf3contentTff7b72=Te6edf3contentTb4b4b4,
Te6edf3image_fieldTff7b72=Te6edf3image_fieldTb4b4b4,
Te6edf3audio_fieldTff7b72=Te6edf3audio_fieldTb4b4b4,
Te6edf3file_attachments_fieldTff7b72=Te6edf3file_attachments_fieldTb4b4b4,
Te6edf3telemetry_dataTff7b72=Te6edf3telemetry_dataTb4b4b4,
Te6edf3commandsTff7b72=Te6edf3commandsTb4b4b4,
Te6edf3delivery_methodTff7b72=Te6edf3delivery_methodTb4b4b4,
Te6edf3reply_to_hashTff7b72=Te6edf3reply_to_hashTb4b4b4,
Te6edf3reply_quoted_contentTff7b72=Te6edf3reply_quoted_contentTb4b4b4,
Te6edf3app_extensionsTff7b72=Te6edf3validated_app_extensionsTb4b4b4,
Tb4b4b4)

Te6edf3is_local_self Tff7b72= Te6edf3appTff7b72.Td2a8ff_is_self_lxmf_destinationTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fflxmf_messageTa5d6ff"Tb4b4b4: Te6edf3convert_lxmf_message_to_dictTb4b4b4(
Te6edf3lxmf_messageTb4b4b4,
Te6edf3include_attachmentsTff7b72=Tff7b72FalseTb4b4b4,
Te6edf3reticulumTff7b72=Te6edf3appTff7b72.Td2a8ffreticulumTb4b4b4,
Te6edf3message_routerTff7b72=Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffmessage_router
Tff7b72if Te6edf3appTff7b72.Td2a8ffcurrent_context
Tff7b72else Tff7b72NoneTb4b4b4,
Te6edf3state_overrideTff7b72=Ta5d6ff"Ta5d6ffdeliveredTa5d6ff" Tff7b72if Te6edf3is_local_self Tff7b72else Tff7b72NoneTb4b4b4,
Te6edf3method_overrideTff7b72=Ta5d6ff"Ta5d6fflocalTa5d6ff" Tff7b72if Te6edf3is_local_self Tff7b72else Tff7b72NoneTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3detail Tff7b72= Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ffSending failedTa5d6ff"
Te6edf3status Tff7b72= T79c0ff503
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3eTb4b4b4, Tb4b4b4(Tf85149ValueErrorTb4b4b4, Tf85149LookupErrorTb4b4b4)Tb4b4b4)Tb4b4b4:
Te6edf3status Tff7b72= T79c0ff400
Tff7b72elif Tffa657isinstanceTb4b4b4(Te6edf3eTb4b4b4, Tf85149TimeoutErrorTb4b4b4)Tb4b4b4:
Te6edf3status Tff7b72= T79c0ff503
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3detailTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=Te6edf3statusTb4b4b4,
Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf-messages/reactionsTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_messages_reactionsTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3destination_hash Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4)
Te6edf3target_message_hash Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftarget_message_hashTa5d6ff"Tb4b4b4)
Te6edf3emoji Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffemojiTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3destination_hash Tff7b72or Tff7b72not Te6edf3target_message_hash Tff7b72or Tff7b72not Te6edf3emojiTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdestination_hash, target_message_hash, and emoji are requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3lxmf_message Tff7b72= Tff7b72await Te6edf3appTff7b72.Td2a8ffsend_reactionTb4b4b4(
Te6edf3destination_hashTff7b72=Te6edf3destination_hashTb4b4b4,
Te6edf3target_message_hashTff7b72=Te6edf3target_message_hashTb4b4b4,
Te6edf3emojiTff7b72=Te6edf3emojiTb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fflxmf_messageTa5d6ff"Tb4b4b4: Te6edf3convert_lxmf_message_to_dictTb4b4b4(
Te6edf3lxmf_messageTb4b4b4,
Te6edf3include_attachmentsTff7b72=Tff7b72FalseTb4b4b4,
Te6edf3reticulumTff7b72=Te6edf3appTff7b72.Td2a8ffreticulumTb4b4b4,
Te6edf3message_routerTff7b72=Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffmessage_router
Tff7b72if Te6edf3appTff7b72.Td2a8ffcurrent_context
Tff7b72else Tff7b72NoneTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3detail Tff7b72= Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ffReaction failedTa5d6ff"
Te6edf3status Tff7b72= T79c0ff503
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3eTb4b4b4, Tb4b4b4(Tf85149ValueErrorTb4b4b4, Tf85149LookupErrorTb4b4b4)Tb4b4b4)Tb4b4b4:
Te6edf3status Tff7b72= T79c0ff400
Tff7b72elif Tffa657isinstanceTb4b4b4(Te6edf3eTb4b4b4, Tf85149TimeoutErrorTb4b4b4)Tb4b4b4:
Te6edf3status Tff7b72= T79c0ff503
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3detailTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=Te6edf3statusTb4b4b4,
Tb4b4b4)

T8b949e# cancel sending lxmf message

T8b949e# cancel sending lxmf message
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf-messages/Tffd700{hash}Ta5d6ff/cancelTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_messages_cancelTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# get path params
Te6edf3message_hash Tff7b72= Te6edf3requestTff7b72.Td2a8ffmatch_infoTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)

T8b949e# convert hash to bytes
Te6edf3hash_as_bytes Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3message_hashTb4b4b4)

T8b949e# cancel outbound message by lxmf message hash
Te6edf3appTff7b72.Td2a8ffmessage_routerTff7b72.Td2a8ffcancel_outboundTb4b4b4(Te6edf3hash_as_bytesTb4b4b4)

T8b949e# get lxmf message from database
Te6edf3lxmf_message Tff7b72= Tff7b72None
Te6edf3db_lxmf_message Tff7b72= Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_lxmf_message_by_hashTb4b4b4(
Te6edf3message_hashTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3db_lxmf_message Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Te6edf3lxmf_message Tff7b72= Te6edf3convert_db_lxmf_message_to_dictTb4b4b4(Te6edf3db_lxmf_messageTb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_messageTa5d6ff"Tb4b4b4: Te6edf3lxmf_messageTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# identify self on existing nomadnetwork link

T8b949e# delete lxmf message
Tf0883e@routesTff7b72.Td2a8ffdeleteTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf-messages/Tffd700{hash}Ta5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_messages_deleteTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# get path params
Te6edf3message_hash Tff7b72= Te6edf3requestTff7b72.Td2a8ffmatch_infoTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)

T8b949e# hash is required
Tff7b72if Te6edf3message_hash Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffhash is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)

T8b949e# delete lxmf messages from db where hash matches
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffdelete_lxmf_message_by_hashTb4b4b4(Te6edf3message_hashTb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# serve lxmf messages for conversation

T8b949e# serve lxmf messages for conversation
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf-messages/conversation/Tffd700{destination_hash}Ta5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_messages_conversationTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# get path params
Te6edf3destination_hash Tff7b72= Te6edf3requestTff7b72.Td2a8ffmatch_infoTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3order Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fforderTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffascTa5d6ff"Tb4b4b4)
Te6edf3count Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcountTa5d6ff"Tb4b4b4)
Te6edf3after_id Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffafter_idTa5d6ff"Tb4b4b4)

Te6edf3local_hash Tff7b72= Te6edf3appTff7b72.Td2a8fflocal_lxmf_destinationTff7b72.Td2a8ffhashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)

Te6edf3results Tff7b72= Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffto_threadTb4b4b4(
Te6edf3appTff7b72.Td2a8ffmessage_handlerTff7b72.Td2a8ffget_conversation_messagesTb4b4b4,
Te6edf3local_hashTb4b4b4,
Te6edf3destination_hashTb4b4b4,
Te6edf3limitTff7b72=Tffa657minTb4b4b4(Tffa657intTb4b4b4(Te6edf3countTb4b4b4)Tb4b4b4, T79c0ff1000Tb4b4b4) Tff7b72if Te6edf3count Tff7b72else T79c0ff100Tb4b4b4,
Te6edf3after_idTff7b72=Te6edf3after_id Tff7b72if Te6edf3order Tff7b72== Ta5d6ff"Ta5d6ffascTa5d6ff" Tff7b72else Tff7b72NoneTb4b4b4,
Te6edf3before_idTff7b72=Te6edf3after_id Tff7b72if Te6edf3order Tff7b72== Ta5d6ff"Ta5d6ffdescTa5d6ff" Tff7b72else Tff7b72NoneTb4b4b4,
Tb4b4b4)

T8b949e# convert to response json
Te6edf3lxmf_messages Tff7b72= Tb4b4b4[
Te6edf3convert_db_lxmf_message_to_dictTb4b4b4(Te6edf3db_lxmf_messageTb4b4b4)
Tff7b72for Te6edf3db_lxmf_message Tff7b72in Te6edf3results
Tb4b4b4]

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fflxmf_messagesTa5d6ff"Tb4b4b4: Te6edf3lxmf_messagesTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# fetch lxmf message attachment

T8b949e# fetch lxmf message attachment
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf-messages/attachment/Tffd700{message_hash}Ta5d6ff/Tffd700{attachment_type}Ta5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_message_attachmentTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3message_hash Tff7b72= Te6edf3requestTff7b72.Td2a8ffmatch_infoTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessage_hashTa5d6ff"Tb4b4b4)
Te6edf3attachment_type Tff7b72= Te6edf3requestTff7b72.Td2a8ffmatch_infoTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffattachment_typeTa5d6ff"Tb4b4b4)
Te6edf3file_index Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffile_indexTa5d6ff"Tb4b4b4)

T8b949e# find message from database
Te6edf3db_lxmf_message Tff7b72= Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_lxmf_message_by_hashTb4b4b4(
Te6edf3message_hashTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3db_lxmf_message Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffMessage not foundTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff404Tb4b4b4)

T8b949e# parse fields
Te6edf3fields Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadsTb4b4b4(Te6edf3db_lxmf_messageTb4b4b4[Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4]Tb4b4b4)

T8b949e# handle image
Tff7b72if Te6edf3attachment_type Tff7b72== Ta5d6ff"Ta5d6ffimageTa5d6ff" Tff7b72and Ta5d6ff"Ta5d6ffimageTa5d6ff" Tff7b72in Te6edf3fieldsTb4b4b4:
Te6edf3image_field Tff7b72= Te6edf3fieldsTb4b4b4[Ta5d6ff"Ta5d6ffimageTa5d6ff"Tb4b4b4]
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3image_fieldTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid image attachmentTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3image_bytes_b64 Tff7b72= Te6edf3image_fieldTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffimage_bytesTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3image_bytes_b64Tb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3image_bytes_b64Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffMissing image dataTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3image_data Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(Te6edf3image_bytes_b64Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid image dataTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3allowed_image_types Tff7b72= Tb4b4b4{Ta5d6ff"Ta5d6ffpngTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffjpegTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffjpgTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffgifTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffwebpTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffbmpTa5d6ff"Tb4b4b4}
Te6edf3detected Tff7b72= Te6edf3detect_image_format_from_magicTb4b4b4(Te6edf3image_dataTb4b4b4)
Tff7b72if Te6edf3detected Tff7b72is Tff7b72None Tff7b72or Te6edf3detected Tff7b72not Tff7b72in Te6edf3allowed_image_typesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid image attachmentTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
T8b949e# Serve Content-Type from magic bytes, not the peer-declared type.
Te6edf3image_type Tff7b72= Ta5d6ff"Ta5d6ffjpegTa5d6ff" Tff7b72if Te6edf3detected Tff7b72== Ta5d6ff"Ta5d6ffjpegTa5d6ff" Tff7b72else Te6edf3detected
Tff7b72return Te6edf3webTff7b72.Td2a8ffResponseTb4b4b4(Te6edf3bodyTff7b72=Te6edf3image_dataTb4b4b4, Te6edf3content_typeTff7b72=Ta5d6fffTa5d6ff"Ta5d6ffimage/Tffd700{Te6edf3image_typeTffd700}Ta5d6ff"Tb4b4b4)

T8b949e# handle audio
Tff7b72if Te6edf3attachment_type Tff7b72== Ta5d6ff"Ta5d6ffaudioTa5d6ff" Tff7b72and Ta5d6ff"Ta5d6ffaudioTa5d6ff" Tff7b72in Te6edf3fieldsTb4b4b4:
Te6edf3audio_field Tff7b72= Te6edf3fieldsTb4b4b4[Ta5d6ff"Ta5d6ffaudioTa5d6ff"Tb4b4b4]
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3audio_fieldTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid audio attachmentTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3audio_bytes_b64 Tff7b72= Te6edf3audio_fieldTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffaudio_bytesTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3audio_bytes_b64Tb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3audio_bytes_b64Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffMissing audio dataTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3audio_data Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(Te6edf3audio_bytes_b64Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid audio dataTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffResponseTb4b4b4(
Te6edf3bodyTff7b72=Te6edf3audio_dataTb4b4b4,
Te6edf3content_typeTff7b72=Ta5d6ff"Ta5d6ffapplication/octet-streamTa5d6ff"Tb4b4b4,
Tb4b4b4)

T8b949e# handle file attachments
Tff7b72if Te6edf3attachment_type Tff7b72== Ta5d6ff"Ta5d6fffileTa5d6ff" Tff7b72and Ta5d6ff"Ta5d6fffile_attachmentsTa5d6ff" Tff7b72in Te6edf3fieldsTb4b4b4:
Tff7b72if Te6edf3file_index Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3index Tff7b72= Tffa657intTb4b4b4(Te6edf3file_indexTb4b4b4)
Tff7b72if Te6edf3index Tff7b72< T79c0ff0Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid file indexTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3file_attachments Tff7b72= Te6edf3fieldsTb4b4b4[Ta5d6ff"Ta5d6fffile_attachmentsTa5d6ff"Tb4b4b4]
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3file_attachmentsTb4b4b4, Tffa657listTb4b4b4) Tff7b72or Te6edf3index Tff7b72>Tff7b72= Tffa657lenTb4b4b4(
Te6edf3file_attachmentsTb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid file indexTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3file_attachment Tff7b72= Te6edf3file_attachmentsTb4b4b4[Te6edf3indexTb4b4b4]
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3file_attachmentTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid file attachmentTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3file_bytes_b64 Tff7b72= Te6edf3file_attachmentTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffile_bytesTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3file_bytes_b64Tb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3file_bytes_b64Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffMissing file dataTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3file_data Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(Te6edf3file_bytes_b64Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid file dataTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3raw_name Tff7b72= Te6edf3file_attachmentTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffile_nameTa5d6ff"Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ffdownloadTa5d6ff"
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3raw_nameTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3raw_name Tff7b72= Ta5d6ff"Ta5d6ffdownloadTa5d6ff"
Te6edf3safe_name Tff7b72= Tb4b4b4(
Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3raw_nameTb4b4b4)
Tff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff'Ta5d6ff"Ta5d6ff'Tb4b4b4, Ta5d6ff"Ta5d6ff_Ta5d6ff"Tb4b4b4)
Tff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffea00\rTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffea00\nTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffea00\x00Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ffdownloadTa5d6ff"
Tff7b72return Te6edf3webTff7b72.Td2a8ffResponseTb4b4b4(
Te6edf3bodyTff7b72=Te6edf3file_dataTb4b4b4,
Te6edf3content_typeTff7b72=Ta5d6ff"Ta5d6ffapplication/octet-streamTa5d6ff"Tb4b4b4,
Te6edf3headersTff7b72=Tb4b4b4{
Ta5d6ff"Ta5d6ffContent-DispositionTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff'Ta5d6ffattachment; filename=Ta5d6ff"Tffd700{Te6edf3safe_nameTffd700}Ta5d6ff"Ta5d6ff'Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72except Tb4b4b4(Tf85149ValueErrorTb4b4b4, Tf85149IndexErrorTb4b4b4)Tb4b4b4:
Tff7b72pass

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffAttachment not foundTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff404Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf-messages/Tffd700{message_hash}Ta5d6ff/uriTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_message_uriTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e"""Build a reticulum:// URI; prefer the router cache over DB-only state."""
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787meshchat_utils Tff7b72import Tb4b4b4(
Te6edf3find_lxm_by_content_hash_for_paper_uriTb4b4b4,
Te6edf3hex_identifier_to_bytesTb4b4b4,
Te6edf3lxmf_message_try_paper_uri_stringTb4b4b4,
Te6edf3normalized_meshchat_lxmf_message_hash_hexTb4b4b4,
Tb4b4b4)

Te6edf3raw_hash Tff7b72= Te6edf3requestTff7b72.Td2a8ffmatch_infoTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessage_hashTa5d6ff"Tb4b4b4)
Te6edf3nh Tff7b72= Te6edf3normalized_meshchat_lxmf_message_hash_hexTb4b4b4(Te6edf3raw_hashTb4b4b4)
Tff7b72if Tff7b72not Te6edf3nhTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid message hashTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3hb Tff7b72= Te6edf3hex_identifier_to_bytesTb4b4b4(Te6edf3nhTb4b4b4)
Tff7b72if Te6edf3hb Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid message hashTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)

Te6edf3lxm Tff7b72= Te6edf3find_lxm_by_content_hash_for_paper_uriTb4b4b4(Te6edf3appTff7b72.Td2a8ffmessage_routerTb4b4b4, Te6edf3hbTb4b4b4)

Tff7b72if Tff7b72not Te6edf3lxmTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffOriginal message bytes not available for URI generationTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff404Tb4b4b4,
Tb4b4b4)

Te6edf3uriTb4b4b4, Te6edf3err_detail Tff7b72= Te6edf3lxmf_message_try_paper_uri_stringTb4b4b4(Te6edf3lxmTb4b4b4)
Tff7b72if Tff7b72not Te6edf3uriTb4b4b4:
Te6edf3body Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffCould not serialize this LXMF payload as a Paper URITa5d6ff"Tb4b4b4,
Tb4b4b4}
Tff7b72if Te6edf3err_detailTb4b4b4:
Te6edf3bodyTb4b4b4[Ta5d6ff"Ta5d6ffdetailTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3err_detail
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Te6edf3bodyTb4b4b4, Te6edf3statusTff7b72=T79c0ff422Tb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffuriTa5d6ff"Tb4b4b4: Te6edf3uriTb4b4b4}Tb4b4b4)

T8b949e# delete lxmf messages for conversation

T8b949e# delete lxmf messages for conversation
Tf0883e@routesTff7b72.Td2a8ffdeleteTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf-messages/conversation/Tffd700{destination_hash}Ta5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_messages_conversation_deleteTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# get path params
Te6edf3destination_hash Tff7b72= Te6edf3requestTff7b72.Td2a8ffmatch_infoTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)

T8b949e# get source hash from local lxmf destination
Te6edf3local_hash Tff7b72= Te6edf3appTff7b72.Td2a8fflocal_lxmf_destinationTff7b72.Td2a8ffhashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)

Tff7b72for Te6edf3message_hash Tff7b72in Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8fflist_message_hashes_for_peerTb4b4b4(
Te6edf3destination_hashTb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3appTff7b72.Td2a8ffmessage_routerTff7b72.Td2a8ffcancel_outboundTb4b4b4(Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3message_hashTb4b4b4)Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass

T8b949e# delete lxmf messages from db where "source to destination" or "destination to source"
Te6edf3appTff7b72.Td2a8ffmessage_handlerTff7b72.Td2a8ffdelete_conversationTb4b4b4(Te6edf3local_hashTb4b4b4, Te6edf3destination_hashTb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/conversation-pinsTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_conversation_pins_getTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3peer_hashes Tff7b72= Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_pinned_peer_hashesTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffpeer_hashesTa5d6ff"Tb4b4b4: Te6edf3peer_hashesTb4b4b4}Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/conversation-pins/toggleTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_conversation_pins_toggleTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffinvalid jsonTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Te6edf3destination_hash Tff7b72= Tb4b4b4(
Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4) Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3dataTb4b4b4, Tffa657dictTb4b4b4) Tff7b72else Tff7b72None
Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3destination_hashTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffmissing destination_hashTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3pinned Tff7b72= Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8fftoggle_peer_pinTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffpeer_hashesTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_pinned_peer_hashesTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffpinnedTa5d6ff"Tb4b4b4: Te6edf3pinnedTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# get lxmf conversations

T8b949e# get lxmf conversations
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/conversationsTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_conversations_getTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3not_ready Tff7b72= Te6edf3appTff7b72.Td2a8ff_require_identity_context_readyTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3not_ready Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3not_ready

T8b949e# get query params
Te6edf3search_query Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffsearchTa5d6ff"Tb4b4b4, Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffqTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4)
Te6edf3filter_unread Tff7b72= Te6edf3parse_bool_query_paramTb4b4b4(
Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(
Ta5d6ff"Ta5d6ffunreadTa5d6ff"Tb4b4b4,
Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffilter_unreadTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fffalseTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3filter_failed Tff7b72= Te6edf3parse_bool_query_paramTb4b4b4(
Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(
Ta5d6ff"Ta5d6fffailedTa5d6ff"Tb4b4b4,
Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffilter_failedTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fffalseTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3filter_has_attachments Tff7b72= Te6edf3parse_bool_query_paramTb4b4b4(
Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(
Ta5d6ff"Ta5d6ffhas_attachmentsTa5d6ff"Tb4b4b4,
Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffilter_has_attachmentsTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fffalseTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3folder_id Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffolder_idTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3folder_id Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3folder_id Tff7b72= Tffa657intTb4b4b4(Te6edf3folder_idTb4b4b4)
Tff7b72except Tf85149ValueErrorTb4b4b4:
Te6edf3folder_id Tff7b72= Tff7b72None

T8b949e# get pagination params
Tff7b72tryTb4b4b4:
Te6edf3limit Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflimitTa5d6ff"Tb4b4b4)
Te6edf3limit Tff7b72= Tffa657intTb4b4b4(Te6edf3limitTb4b4b4) Tff7b72if Te6edf3limit Tff7b72is Tff7b72not Tff7b72None Tff7b72else Tff7b72None
Tff7b72except Tf85149ValueErrorTb4b4b4:
Te6edf3limit Tff7b72= Tff7b72None
Te6edf3limit Tff7b72= Te6edf3appTff7b72.Td2a8ffmessage_handlerTff7b72.Td2a8ffclamp_conversations_limitTb4b4b4(Te6edf3limitTb4b4b4)

Tff7b72tryTb4b4b4:
Te6edf3offset Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffoffsetTa5d6ff"Tb4b4b4)
Te6edf3offset Tff7b72= Tffa657intTb4b4b4(Te6edf3offsetTb4b4b4) Tff7b72if Te6edf3offset Tff7b72is Tff7b72not Tff7b72None Tff7b72else T79c0ff0
Tff7b72except Tf85149ValueErrorTb4b4b4:
Te6edf3offset Tff7b72= T79c0ff0

Tff7b72tryTb4b4b4:
Te6edf3local_hash Tff7b72= Te6edf3appTff7b72.Td2a8fflocal_lxmf_destinationTff7b72.Td2a8ffhexhash

Te6edf3db_conversations Tff7b72= Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffto_threadTb4b4b4(
Te6edf3appTff7b72.Td2a8ffmessage_handlerTff7b72.Td2a8ffget_conversationsTb4b4b4,
Te6edf3local_hashTb4b4b4,
Te6edf3searchTff7b72=Te6edf3search_queryTb4b4b4,
Te6edf3filter_unreadTff7b72=Te6edf3filter_unreadTb4b4b4,
Te6edf3filter_failedTff7b72=Te6edf3filter_failedTb4b4b4,
Te6edf3filter_has_attachmentsTff7b72=Te6edf3filter_has_attachmentsTb4b4b4,
Te6edf3folder_idTff7b72=Te6edf3folder_idTb4b4b4,
Te6edf3limitTff7b72=Te6edf3limitTb4b4b4,
Te6edf3offsetTff7b72=Te6edf3offsetTb4b4b4,
Tb4b4b4)

Te6edf3row_dicts Tff7b72= Tb4b4b4[Tb4b4b4]
Te6edf3peer_hashes Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3row Tff7b72in Te6edf3db_conversationsTb4b4b4:
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3rowTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3row Tff7b72= Tffa657dictTb4b4b4(Te6edf3rowTb4b4b4)
Te6edf3other_user_hash Tff7b72= Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffpeer_hashTa5d6ff"Tb4b4b4]
Tff7b72if Te6edf3appTff7b72.Td2a8ff_lxmf_sieve_hides_peerTb4b4b4(
Te6edf3other_user_hashTb4b4b4,
Te6edf3message_titleTff7b72=Te6edf3rowTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftitleTa5d6ff"Tb4b4b4)Tb4b4b4,
Te6edf3message_contentTff7b72=Te6edf3rowTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3row_dictsTff7b72.Td2a8ffappendTb4b4b4(Te6edf3rowTb4b4b4)
Te6edf3peer_hashesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3other_user_hashTb4b4b4)

Te6edf3tracking_states Tff7b72= Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffto_threadTb4b4b4(
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8fftelemetryTff7b72.Td2a8ffget_tracking_statesTb4b4b4,
Te6edf3peer_hashesTb4b4b4,
Tb4b4b4)

Te6edf3conversations Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3row Tff7b72in Te6edf3row_dictsTb4b4b4:
Te6edf3other_user_hash Tff7b72= Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffpeer_hashTa5d6ff"Tb4b4b4]

Te6edf3display_name Tff7b72= Tff7b72None
Tff7b72if Te6edf3rowTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffpeer_app_dataTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3display_name Tff7b72= Te6edf3parse_lxmf_display_nameTb4b4b4(
Te6edf3app_data_base64Tff7b72=Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffpeer_app_dataTa5d6ff"Tb4b4b4]Tb4b4b4,
Te6edf3default_valueTff7b72=Tff7b72NoneTb4b4b4,
Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3display_name Tff7b72and Te6edf3rowTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcontact_nameTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3display_name Tff7b72= Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffcontact_nameTa5d6ff"Tb4b4b4]
Tff7b72if Tff7b72not Te6edf3display_nameTb4b4b4:
Te6edf3display_name Tff7b72= Ta5d6ff"Ta5d6ffAnonymous PeerTa5d6ff"

T8b949e# user icon
Te6edf3user_icon Tff7b72= Tff7b72None
Tff7b72if Te6edf3rowTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fficon_nameTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3user_icon Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6fficon_nameTa5d6ff"Tb4b4b4: Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6fficon_nameTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffforeground_colourTa5d6ff"Tb4b4b4: Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffforeground_colourTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffbackground_colourTa5d6ff"Tb4b4b4: Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffbackground_colourTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4}

T8b949e# contact image blob stays out of the list payload
Te6edf3has_contact_image Tff7b72= Tffa657boolTb4b4b4(Te6edf3rowTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffhas_contact_imageTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4)

Tff7b72tryTb4b4b4:
Te6edf3is_unread Tff7b72= Te6edf3compute_lxmf_conversation_unread_from_latest_rowTb4b4b4(
Te6edf3rowTb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Te6edf3is_unread Tff7b72= Tff7b72False

T8b949e# Add extra check for notification viewed state if unread
Tff7b72if Te6edf3is_unread Tff7b72and Te6edf3filter_unreadTb4b4b4:
Tff7b72if Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffis_notification_viewedTb4b4b4(
Te6edf3other_user_hashTb4b4b4,
Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6fftimestampTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Te6edf3is_unread Tff7b72= Tff7b72False
Tff7b72if Te6edf3filter_unreadTb4b4b4:
Tff7b72continue T8b949e# Skip this conversation if filtering unread and it's actually viewed

Te6edf3has_attachments Tff7b72= Tffa657boolTb4b4b4(
Te6edf3rowTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffhas_attachmentsTa5d6ff"Tb4b4b4) Tff7b72in Tb4b4b4(T79c0ff1Tb4b4b4, Tff7b72TrueTb4b4b4, Ta5d6ff"Ta5d6ff1Ta5d6ff"Tb4b4b4)
Tff7b72or Te6edf3message_fields_have_attachmentsTb4b4b4(Te6edf3rowTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# add to conversations
Te6edf3conversationsTff7b72.Td2a8ffappendTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffdisplay_nameTa5d6ff"Tb4b4b4: Te6edf3display_nameTb4b4b4,
Ta5d6ff"Ta5d6ffcustom_display_nameTa5d6ff"Tb4b4b4: Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffcustom_display_nameTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffcontact_imageTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffhas_contact_imageTa5d6ff"Tb4b4b4: Te6edf3has_contact_imageTb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3other_user_hashTb4b4b4,
Ta5d6ff"Ta5d6ffis_unreadTa5d6ff"Tb4b4b4: Te6edf3is_unreadTb4b4b4,
Ta5d6ff"Ta5d6ffis_trackingTa5d6ff"Tb4b4b4: Te6edf3tracking_statesTff7b72.Td2a8ffgetTb4b4b4(Te6edf3other_user_hashTb4b4b4, Tff7b72FalseTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fffailed_messages_countTa5d6ff"Tb4b4b4: Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6fffailed_countTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffhas_attachmentsTa5d6ff"Tb4b4b4: Te6edf3has_attachmentsTb4b4b4,
Ta5d6ff"Ta5d6fflatest_message_titleTa5d6ff"Tb4b4b4: Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6fftitleTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6fflatest_message_previewTa5d6ff"Tb4b4b4: Te6edf3lxmf_sidebar_preview_for_conversation_latest_rowTb4b4b4(
Te6edf3rowTb4b4b4,
Te6edf3local_hashTff7b72=Te6edf3local_hashTb4b4b4,
Te6edf3peer_display_nameTff7b72=Tb4b4b4(
Te6edf3rowTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcustom_display_nameTa5d6ff"Tb4b4b4)
Tff7b72or Te6edf3display_name
Tff7b72or Ta5d6ff"Ta5d6ffAnonymous PeerTa5d6ff"
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fflatest_message_created_atTa5d6ff"Tb4b4b4: Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_user_iconTa5d6ff"Tb4b4b4: Te6edf3user_iconTb4b4b4,
Ta5d6ff"Ta5d6ffis_contactTa5d6ff"Tb4b4b4: Tffa657boolTb4b4b4(Te6edf3rowTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffis_contactTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffupdated_atTa5d6ff"Tb4b4b4: Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffconversationsTa5d6ff"Tb4b4b4: Te6edf3conversationsTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError in lxmf_conversations_get: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Te6edf3detail Tff7b72= Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)
Te6edf3status Tff7b72= Tb4b4b4(
T79c0ff503
Tff7b72if Tb4b4b4(
Tffa657isinstanceTb4b4b4(Te6edf3eTb4b4b4, Te6edf3sqlite3Tff7b72.Td2a8ffOperationalErrorTb4b4b4)
Tff7b72or Ta5d6ff"Ta5d6ffunable to open database fileTa5d6ff" Tff7b72in Te6edf3detail
Tff7b72or Ta5d6ff"Ta5d6ffdatabase is lockedTa5d6ff" Tff7b72in Te6edf3detail
Tb4b4b4)
Tff7b72else T79c0ff500
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6ff"Ta5d6ffDatabase temporarily unavailable. Retry shortly.Ta5d6ff"
Tff7b72if Te6edf3status Tff7b72== T79c0ff503
Tff7b72else Ta5d6ff"Ta5d6ffFailed to load conversationsTa5d6ff"
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=Te6edf3statusTb4b4b4,
Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/foldersTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_folders_getTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3folders Tff7b72= Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_all_foldersTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4[Tffa657dictTb4b4b4(Te6edf3fTb4b4b4) Tff7b72for Te6edf3f Tff7b72in Te6edf3foldersTb4b4b4]Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/foldersTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_folders_postTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3name Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3nameTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffName is requiredTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffcreate_folderTb4b4b4(Te6edf3nameTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffFolder createdTa5d6ff"Tb4b4b4}Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff500Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpatchTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/folders/Tffd700{id}Ta5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_folders_patchTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3folder_id Tff7b72= Tffa657intTb4b4b4(Te6edf3requestTff7b72.Td2a8ffmatch_infoTb4b4b4[Ta5d6ff"Ta5d6ffidTa5d6ff"Tb4b4b4]Tb4b4b4)
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3name Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3nameTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffName is requiredTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffrename_folderTb4b4b4(Te6edf3folder_idTb4b4b4, Te6edf3nameTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffFolder renamedTa5d6ff"Tb4b4b4}Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffdeleteTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/folders/Tffd700{id}Ta5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_folders_deleteTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3folder_id Tff7b72= Tffa657intTb4b4b4(Te6edf3requestTff7b72.Td2a8ffmatch_infoTb4b4b4[Ta5d6ff"Ta5d6ffidTa5d6ff"Tb4b4b4]Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffdelete_folderTb4b4b4(Te6edf3folder_idTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffFolder deletedTa5d6ff"Tb4b4b4}Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/sieve-filtersTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_sieve_filters_getTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3raw Tff7b72= Te6edf3appTff7b72.Td2a8ffconfigTff7b72.Td2a8fflxmf_sieve_filters_jsonTff7b72.Td2a8ffgetTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fffiltersTa5d6ff"Tb4b4b4: Te6edf3parse_lxmf_sieve_filters_jsonTb4b4b4(Te6edf3rawTb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffputTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/sieve-filtersTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_sieve_filters_putTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3filters Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffiltersTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3filtersTb4b4b4, Tffa657listTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fffilters must be a listTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3normalized Tff7b72= Te6edf3normalize_lxmf_sieve_filtersTb4b4b4(Te6edf3filtersTb4b4b4)
Te6edf3folder_rows Tff7b72= Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_all_foldersTb4b4b4(Tb4b4b4)
Te6edf3valid_folder_ids Tff7b72= Tb4b4b4{Te6edf3fTb4b4b4[Ta5d6ff"Ta5d6ffidTa5d6ff"Tb4b4b4] Tff7b72for Te6edf3f Tff7b72in Te6edf3folder_rowsTb4b4b4}
Tff7b72for Te6edf3r Tff7b72in Te6edf3normalizedTb4b4b4:
Tff7b72if Te6edf3rTb4b4b4[Ta5d6ff"Ta5d6ffactionTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6fffolderTa5d6ff" Tff7b72and Te6edf3rTb4b4b4[Ta5d6ff"Ta5d6fffolder_idTa5d6ff"Tb4b4b4] Tff7b72not Tff7b72in Te6edf3valid_folder_idsTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffUnknown folder_id Tffd700{Te6edf3rTb4b4b4[Ta5d6ff'Ta5d6fffolder_idTa5d6ff'Tb4b4b4]Tffd700}Ta5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffconfigTff7b72.Td2a8fflxmf_sieve_filters_jsonTff7b72.Td2a8ffsetTb4b4b4(Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(Te6edf3normalizedTb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fffiltersTa5d6ff"Tb4b4b4: Te6edf3normalizedTb4b4b4}Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/conversations/move-to-folderTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_conversations_move_to_folderTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3peer_hashes Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffpeer_hashesTa5d6ff"Tb4b4b4, Tb4b4b4[Tb4b4b4]Tb4b4b4)
Te6edf3folder_id Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffolder_idTa5d6ff"Tb4b4b4) T8b949e# Can be None to remove from folder
Tff7b72if Tff7b72not Te6edf3peer_hashesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffpeer_hashes is requiredTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffmove_conversations_to_folderTb4b4b4(Te6edf3peer_hashesTb4b4b4, Te6edf3folder_idTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffConversations movedTa5d6ff"Tb4b4b4}Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/conversations/bulk-mark-as-readTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_conversations_bulk_mark_readTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3mark_all Tff7b72= Tffa657boolTb4b4b4(Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmark_allTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3destination_hashes Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashesTa5d6ff"Tb4b4b4, Tb4b4b4[Tb4b4b4]Tb4b4b4)
Tff7b72if Te6edf3mark_allTb4b4b4:
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffmark_all_conversations_as_readTb4b4b4(Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffmark_all_notifications_as_viewedTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffAll conversations marked as readTa5d6ff"Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3destination_hashesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdestination_hashes is requiredTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffmark_conversations_as_readTb4b4b4(Te6edf3destination_hashesTb4b4b4)
T8b949e# Keep notification viewed state in sync so the bell never
T8b949e# disagrees with the conversation list.
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffmark_all_notifications_as_viewedTb4b4b4(Te6edf3destination_hashesTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffConversations marked as readTa5d6ff"Tb4b4b4}Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/conversations/bulk-deleteTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_conversations_bulk_deleteTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3destination_hashes Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashesTa5d6ff"Tb4b4b4, Tb4b4b4[Tb4b4b4]Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3destination_hashesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdestination_hashes is requiredTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3local_hash Tff7b72= Te6edf3appTff7b72.Td2a8fflocal_lxmf_destinationTff7b72.Td2a8ffhexhash
Tff7b72for Te6edf3dest_hash Tff7b72in Te6edf3destination_hashesTb4b4b4:
Tff7b72for Te6edf3message_hash Tff7b72in Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8fflist_message_hashes_for_peerTb4b4b4(
Te6edf3dest_hashTb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3appTff7b72.Td2a8ffmessage_routerTff7b72.Td2a8ffcancel_outboundTb4b4b4(Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3message_hashTb4b4b4)Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Te6edf3appTff7b72.Td2a8ffmessage_handlerTff7b72.Td2a8ffdelete_conversationTb4b4b4(Te6edf3local_hashTb4b4b4, Te6edf3dest_hashTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffConversations deletedTa5d6ff"Tb4b4b4}Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/folders/exportTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_folders_exportTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3folders Tff7b72= Tb4b4b4[Tffa657dictTb4b4b4(Te6edf3fTb4b4b4) Tff7b72for Te6edf3f Tff7b72in Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_all_foldersTb4b4b4(Tb4b4b4)Tb4b4b4]
Te6edf3mappings Tff7b72= Tb4b4b4[
Tffa657dictTb4b4b4(Te6edf3mTb4b4b4) Tff7b72for Te6edf3m Tff7b72in Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_all_conversation_foldersTb4b4b4(Tb4b4b4)
Tb4b4b4]
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fffoldersTa5d6ff"Tb4b4b4: Te6edf3foldersTb4b4b4, Ta5d6ff"Ta5d6ffmappingsTa5d6ff"Tb4b4b4: Te6edf3mappingsTb4b4b4}Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/folders/importTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_folders_importTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3folders Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffoldersTa5d6ff"Tb4b4b4, Tb4b4b4[Tb4b4b4]Tb4b4b4)
Te6edf3mappings Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmappingsTa5d6ff"Tb4b4b4, Tb4b4b4[Tb4b4b4]Tb4b4b4)

T8b949e# We'll try to recreate folders by name to avoid ID conflicts
Te6edf3folder_name_to_new_id Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72for Te6edf3f Tff7b72in Te6edf3foldersTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffcreate_folderTb4b4b4(Te6edf3fTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3loggerTff7b72.Td2a8ffdebugTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFolder Ta5d6ff'Tffd700{Te6edf3fTb4b4b4[Ta5d6ff'Ta5d6ffnameTa5d6ff'Tb4b4b4]Tffd700}Ta5d6ff'Ta5d6ff likely already exists: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4)

T8b949e# Refresh folder list to get new IDs
Te6edf3all_folders Tff7b72= Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffget_all_foldersTb4b4b4(Tb4b4b4)
Tff7b72for Te6edf3f Tff7b72in Te6edf3all_foldersTb4b4b4:
Te6edf3folder_name_to_new_idTb4b4b4[Te6edf3fTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4]Tb4b4b4] Tff7b72= Te6edf3fTb4b4b4[Ta5d6ff"Ta5d6ffidTa5d6ff"Tb4b4b4]

T8b949e# Map old IDs to new IDs if possible, or just use names if we had them
T8b949e# Since IDs might change, we should have exported names too
T8b949e# Let's assume the export had folder names in mappings or we match by old folder info
Te6edf3old_id_to_name Tff7b72= Tb4b4b4{Te6edf3fTb4b4b4[Ta5d6ff"Ta5d6ffidTa5d6ff"Tb4b4b4]Tb4b4b4: Te6edf3fTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4] Tff7b72for Te6edf3f Tff7b72in Te6edf3foldersTb4b4b4}

Tff7b72for Te6edf3m Tff7b72in Te6edf3mappingsTb4b4b4:
Te6edf3peer_hash Tff7b72= Te6edf3mTb4b4b4[Ta5d6ff"Ta5d6ffpeer_hashTa5d6ff"Tb4b4b4]
Te6edf3old_folder_id Tff7b72= Te6edf3mTb4b4b4[Ta5d6ff"Ta5d6fffolder_idTa5d6ff"Tb4b4b4]
Te6edf3folder_name Tff7b72= Te6edf3old_id_to_nameTff7b72.Td2a8ffgetTb4b4b4(Te6edf3old_folder_idTb4b4b4)
Tff7b72if Te6edf3folder_name Tff7b72and Te6edf3folder_name Tff7b72in Te6edf3folder_name_to_new_idTb4b4b4:
Te6edf3new_folder_id Tff7b72= Te6edf3folder_name_to_new_idTb4b4b4[Te6edf3folder_nameTb4b4b4]
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffmove_conversation_to_folderTb4b4b4(
Te6edf3peer_hashTb4b4b4,
Te6edf3new_folder_idTb4b4b4,
Tb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffFolders and mappings importedTa5d6ff"Tb4b4b4}Tb4b4b4)

T8b949e# mark lxmf conversation as read

T8b949e# mark lxmf conversation as read
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/lxmf/conversations/Tffd700{destination_hash}Ta5d6ff/mark-as-readTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflxmf_conversations_mark_readTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# get path params
Te6edf3destination_hash Tff7b72= Te6edf3requestTff7b72.Td2a8ffmatch_infoTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)

T8b949e# mark lxmf conversation as read
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffmark_conversation_as_readTb4b4b4(Te6edf3destination_hashTb4b4b4)
T8b949e# Keep notification viewed state in sync so the bell never
T8b949e# disagrees with the conversation list.
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffmark_notification_as_viewedTb4b4b4(Te6edf3destination_hashTb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# mark notifications as viewed


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────